/*
 * DrawPane.c
 *
 *  Created on: 09.06.2011
 *      Author: sili
 */

#include "DrawPane.h"
#include "AALItem.h"
#include "Visualizer.h"
#include "curl/curl.h"

static log4cxx::LoggerPtr logger(log4cxx::Logger::getLogger("at.sili.aalsystem.visualization.DrawPane"));

DrawPane::DrawPane(cPixmap * _pixmap, AALArea * _area, uint32_t _colorBg, PaneType _type, const char * _name){
	AAL_DEBUG(logger, "%s constructor using trueColor and AALArea: left:%f top:%f width:%f heght:%f",
			  _name, _area->left, _area->top, _area->width, _area->height);
	this->pixmapPane = _pixmap;
	this->area.adopt(_area);
	this->colorBg = _colorBg;
	this->paneType = _type;
	this->name.clear();
	this->name.append(_name);
	cPixmap::Lock();
	// here	                         a    r    g    b
	// uint32_t tColor = ArgbToColor(0x10,0x80,0xFF,0xFF);
	this->pixmapPane->Fill(this->colorBg);
	// this->pixmapPane->SetAlpha(90);
	cPixmap::Unlock();
	this->imagePane = NULL;
}

DrawPane::DrawPane(const int _bpp, AALArea * _area, uint32_t _colorBg, PaneType _type, const char * _name){
	AAL_DEBUG(logger, "%s constructor limited %d nr. of colors and AALArea: left:%f top:%f width:%f heght:%f",
			  _name, _bpp, _area->left, _area->top, _area->width, _area->height);
	this->bpp = _bpp;
	this->area.adopt(_area);
	this->colorBg = _colorBg;
	this->paneType = _type;
	this->name.clear();
	this->name.append(_name);

	int t = int(floor(area.top));
	int l = int(floor(area.left));
	int w = int(floor(area.width));
	int h = int(floor(area.height));

	Geometry geometry(w, h, l, t, false, false);

	// create RGBA color for magick
	int a = 0x000000FF & this->colorBg >> 24;
	int r = 0x000000FF & this->colorBg >> 16;
	int g = 0x000000FF & this->colorBg >> 8;
	int b = 0x000000FF & this->colorBg;
	//Color bgColor(r,g,b,a);
	Color bgColor(0,0,0,TransparentOpacity);

	// Color colorBg(0,0,0,TransparentOpacity);
	// this->imagePane = new Image(geometry, colorBg);
	this->imagePane = new Image(geometry, bgColor);
	this->imagePane->type(TrueColorMatteType);
	this->imagePane->syncPixels();
	this->pixmapPane = NULL;
}

DrawPane::~DrawPane(){
	AAL_DEBUG(logger, "%s destructor", this->name.c_str());
	delete(this->imagePane);
}

void DrawPane::DrawHotkey(AALLocation & _location, AALItem * _aalItem){
	// keep it simple for prototyping, place it at the upper left corner

	char aalHotkey[3];
	snprintf(aalHotkey, 3, "%d", _aalItem->getHotkey());

	const cFont * font = cFont::GetFont(fontOsd);
	int w = font->Width(aalHotkey);
	int h = font->Height(aalHotkey);

	/*
	int radius = 0;
	#ifdef REELVDR
		radius = font->Size();
	#else
		radius = font->Size();
	#endif
	*/

	double radius = 20;
	if(w > h){
		radius = w / 2.0;
	} else {
		radius = h / 2.0;
	}


	double xCenter = _location.left;
	double yCenter = _location.top;

	double x1 = xCenter - radius;
	double y1 = yCenter - radius;
	// double x2 = xCenter + radius;
	// double y2 = yCenter + radius;

	if(this->pixmapPane != NULL){
		cPixmap::Lock();
		this->pixmapPane->DrawEllipse(cRect(x1, y1, radius * 2.0, radius * 2.0), clrRed);
		//this->pixmapPane->DrawText(cPoint(xCenter-w/2.0,yCenter-h/2.0), aalHotkey, clrWhite, clrTransparent, font);
		this->pixmapPane->DrawText(cPoint(xCenter-radius/2.0,yCenter-radius/2.0), aalHotkey, clrWhite, clrTransparent, font);
		cPixmap::Unlock();
	} else if(this->imagePane != NULL){
		// need to adjust the postitions cause I'm drawing on osd not on a subelement such an pixmapPane
		// this->osd->DrawEllipse(x1, y1, x2, y2, clrRed);
		// this->osd->DrawText(xCenter-w/2.0, yCenter-h/2.0, aalHotkey, clrWhite, clrTransparent, font);
		// Set draw options
		this->imagePane->strokeColor("white"); // Outline color
		this->imagePane->fillColor("red"); // Fill color
		this->imagePane->strokeWidth(1);

		// Draw a circle
		this->imagePane->draw( DrawableCircle(xCenter,yCenter, x1, y1));
		// DrawText(cPoint(xCenter-w/2.0, yCenter-h/2.0),aalHotkey,clrWhite, clrTransparent, font);
		DrawText(cPoint(xCenter-radius + w / 2.0, yCenter-radius),aalHotkey,clrWhite, clrTransparent, font);
	}
}

void DrawPane::DrawText(AALArea & _area, const char * _text, tColor _colorFg, tColor _colorBg, const cFont * _font, int _alignment){
	AAL_TRACE(logger, "%s drawing text at left:%f top:%f width:%f height:%f", this->name.c_str(), _area.left,_area.top,_area.width,_area.height);

	int w = _font->Width(_text);
	int h = _font->Height(_text);

	int maxTextWidth = _area.width;

	vector<string> lines;
	if (w > maxTextWidth){
		// need to split text into several lines
		vector<string> words;
		string message;
		message.clear();
		message.append(_text);
		Tools::split((char*)message.c_str(), " ", &words);
		unsigned int start = 0, stop = 0;
		string temp;
		for (unsigned int index=0; index < words.size(); index++){
			char * word = (char*)words[index].c_str();
			temp.append(word);
			temp.append(" ");

			int currentWidth = _font->Width(temp.c_str());
			if(currentWidth > maxTextWidth){
				// we are too big
				// go 1 word back and finish the line
				stop = index;
				string currentLine;
				for (unsigned int walker = start; walker < stop; walker++){
					currentLine.append((char*)words[walker].c_str());
					currentLine.append(" ");
				}
				lines.push_back(currentLine.c_str());
				start = stop;
				temp.clear();
				temp.append(word);
				temp.append(" ");
			}
		}
		// get the rest of the word which fitts into the width
		lines.push_back(temp.c_str());
	} else {
		lines.push_back(_text);
	}

	for (unsigned int index=0; index < lines.size(); index++){
		const char * line = (char*)lines[index].c_str();
		this->DrawText(cPoint(_area.left, _area.top + index * h), line, _colorFg, _colorBg, _font, _area.width, _area.height);
	}
}



void DrawPane::DrawText(const cPoint & _point, const char * s, tColor _colorFg, tColor _colorBg, const cFont * _font, int _width, int _height, int _alignment){
	AAL_TRACE(logger, "Drawing text %s at left:%d top:%d width:%d height:%d", s, _point.X(), _point.Y(), _width, _height);

	string sr(s);
	string dest(curl_unescape(s,sr.size()));

	/*
	for (int i = 0; i < dest.size(); i++){
		printf ("DEST %d %c %X\n", i, dest[i], dest[i]);
	}
	*/


	unsigned line_count = 1;
	string::iterator end_it = utf8::find_invalid(dest.begin(), dest.end());
	if (end_it != dest.end()) {
		AAL_WARN(logger, "Invalid UTF-8 encoding detected at line %d ", line_count);
		AAL_WARN(logger, "This part is fine %s ", string(dest.begin(), end_it).c_str());
	}

	std::string temp;
	utf8::replace_invalid(dest.begin(), dest.end(), back_inserter(temp));
	string utf8line(temp);

	if(this->pixmapPane != NULL){
		cPixmap::Lock();
		this->pixmapPane->DrawText(_point, utf8line.c_str(), _colorFg, _colorBg, _font, _width, _height, _alignment);
		cPixmap::Unlock();
	} else if(this->imagePane != NULL){
		// need to adjust the postitions cause I'm drawing on osd not on a subelement such an pixmapPane
		// this->osd->DrawText(_point.X() + this->area.left, _point.Y() + this->area.top, s, _colorFg, _colorBg, _font, _width, _height, _alignment);
		// Set draw options
		// TODO use given _colorFg and _colorBg
		// this->imagePane->strokeColor("white"); // Outline color
		this->imagePane->strokeWidth(0.0);
		this->imagePane->strokeColor(Color(0, 0, 0, MaxRGB));

		if(_colorBg != clrTransparent){
			int r = 0x000000FF & _colorBg >> 16;
			int g = 0x000000FF & _colorBg >> 8;
			int b = 0x000000FF & _colorBg;
			Color fillColor(r,g,b);
			DrawableRectangle bgRectangle(_point.X(), _point.Y(), _point.X() + _width, _point.Y() + _height);
			AAL_DEBUG(logger, "Drawing rectangle at left:%d top:%d width:%d height:%d", _point.X(), _point.Y(), _point.X() + _width, _point.Y() + _height);
			this->imagePane->fillColor(fillColor);
			this->imagePane->draw(bgRectangle);
		}

		int r = 0x000000FF & _colorFg >> 16;
		int g = 0x000000FF & _colorFg >> 8;
		int b = 0x000000FF & _colorFg;
		Color fillColor(r,g,b);
		this->imagePane->fillColor(fillColor);
		// JUST OUTLINE this->imagePane->fillColor(Color(0, 0, 0, MaxRGB)); // Outline color
		// this->imagePane->strokeWidth(0);
		this->imagePane->font(_font->FontName());


		//#ifdef REELVDR
		//		this->imagePane->fontPointsize(_font->Size() * 1.3);
		//#else
		//		this->imagePane->fontPointsize(_font->Size() * 0.9);
		//#endif
		this->imagePane->fontPointsize(_font->Size());

		//int size = strlen(s);
		//char buff[size+1];
		//Utf8Strn0Cpy(buff,s,size+1);

		int xPos = _point.X();
		int yPos = _point.Y();

		if(_alignment == taCenter){
			TypeMetric metric;
			this->imagePane->fontTypeMetrics(utf8line,&metric);
			int xOffset = (_width - metric.textWidth()) / 2.0;
			int yOffset = (_height - metric.textHeight()) / 2.0;
			if(xOffset > 0.0)
				xPos += xOffset;
			if(yOffset > 0.0)
				yPos += yOffset;
		}

		// hack for the draw of Magick++
		// origin e.g. in text starts with the lower left point
		// pixmap uses the upper left point, so arrange y position for Magick++
		AAL_TRACE(logger, "RealDrawing at x:%d y:%d line:%s", xPos, yPos + (int)_font->Size(), utf8line.c_str());
		DrawableText textToDraw(xPos, yPos + (int)_font->Size(), utf8line);
		// DrawableTextEncoding("UTF-8")
		this->imagePane->draw(textToDraw);
	}
}

void DrawPane::DrawImage(const cPoint & _point, Image * _image, int _visibility){
	int w = _image->columns();
	int h = _image->rows();
	AAL_TRACE(logger, "%s drwaing image at point x:%d y:%d w:%d h:%d", this->name.c_str(), _point.X(), _point.Y(), w, h);
	if(this->pixmapPane != NULL){
		AAL_TRACE(logger, "%s using cImage", this->name.c_str());
		cImage img(cSize(w,h));
		for (int x = 0; x < w; x++){
			for (int y = 0; y < h; y++) {

				const PixelPacket * ppp = _image->getConstPixels(x, y, 1, 1);

				// Color replacement...
				uint32_t tColor = ArgbToColor(ppp->opacity,ppp->red,ppp->green,ppp->blue);
				img.SetPixel(cPoint(x, y), tColor);


				/*
				Color c = _image->pixelColor(x,y);
				// alpha 0==transparent, 255==opacue
				Quantum alphaQuantum = c.alphaQuantum();
				Quantum redQuantum = c.redQuantum();
				Quantum greenQuantum = c.greenQuantum();
				Quantum blueQuantum = c.blueQuantum();
				// here	                         a              r          g            b
				uint32_t tColor = ArgbToColor(0xFF-alphaQuantum,redQuantum,greenQuantum,blueQuantum);
				img.SetPixel(cPoint(x, y), tColor);
				*/
			}
		}
		cPixmap::Lock();
		this->pixmapPane->DrawImage(_point, img);
		cPixmap::Unlock();
	} else if(this->imagePane != NULL){
		AAL_TRACE(logger, "%s using Magick Image at position x:%d, y:%d, image is:%d", this->name.c_str(),_point.X(), _point.Y(), (int)_image);
		this->imagePane->composite(*_image,_point.X(), _point.Y(),OverCompositeOp);
	}
}

void DrawPane::Clear(){
	AAL_TRACE(logger, "Clear %s", this->name.c_str());
	if(this->pixmapPane != NULL){
		cPixmap::Lock();
		this->pixmapPane->Clear();
		this->pixmapPane->Fill(this->colorBg);
		cPixmap::Unlock();
	} else if(this->imagePane != NULL){
		this->imagePane->erase();
		if(this->colorBg != 0){
			int a = 0x000000FF & this->colorBg >> 24;
			int r = 0x000000FF & this->colorBg >> 16;
			int g = 0x000000FF & this->colorBg >> 8;
			int b = 0x000000FF & this->colorBg;
			AAL_TRACE(logger, "%s, setting background to a:%X r:%X g:%X b:%X",this->name.c_str(), a, r, g, b);
			// here	    r    g    b    a
			// Color colorBg = Color(r, g, b, a);
			Color colorBg = Color(r,g,b,a);

			for (unsigned int x = 0; x < this->imagePane->columns(); x++){
				for (unsigned int y = 0; y < this->imagePane->rows(); y++) {
				    this->imagePane->pixelColor(x,y,colorBg);
				}
			}
		}
	}
}

bool DrawPane::Intersect(int _x, int _y){
	if(_x >= this->area.left && _x <= this->area.left + this->area.width &&
		_y >= this->area.top && _y <= this->area.top + this->area.height)
		return true;
	return false;
}

